const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

{
  // 简单实现
  function MyPromise(executor) {
    this.value = null;
    this.state = PENDING;
    this.resolveCbs = [];
    this.rejectCbs = [];

    const resolve = (value) => {
      // setTimeout 模拟 then 的异步
      setTimeout(() => {
        if (this.state === PENDING) {
          this.state = FULFILLED;
          this.value = value;
          this.resolveCbs.map((cb) => cb(value));
        }
      });
    };

    const reject = (value) => {
      setTimeout(() => {
        if (this.state === PENDING) {
          this.state = REJECTED;
          this.value = value;
          this.rejectCbs.map((cb) => cb(value));
        }
      });
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  MyPromise.prototype.then = function (onFULFILLED, onRejected) {
    if (this.state === PENDING) {
      this.resolveCbs.push(onFULFILLED);
      this.rejectCbs.push(onRejected);
    }
    if (this.state === FULFILLED) onFULFILLED(this.value);
    if (this.state === REJECTED) onRejected(this.value);
  };

  // 测试
  // new MyPromise((resolve, reject) => {
  //   console.log('start');
  //   resolve(1);
  // }).then((res) => {
  //   console.log('then');
  //   console.log(res);
  // });
  // console.log('end');
}

{
  // 相对复杂实现
  class MyPromise {
    constructor(executor) {
      // Promise的状态改变
      this.state = PENDING;

      //resolve的值
      this.value = undefined;
      //reject的原因
      this.reason = undefined;

      this.onResolveList = [];
      this.onRejectList = [];

      let resolve = (value) => {
        // 通过queueMicrotask来执行微任务，保证promise执行顺序
        queueMicrotask(() => {
          // 保证Promise的状态改变后永远不会再变
          if (this.state === PENDING) {
            this.state = FULFILLED;
            this.value = value;
            //发布
            this.runTask();
          }
        });
      };

      let reject = (reason) => {
        queueMicrotask(() => {
          if (this.state === PENDING) {
            this.state = REJECTED;
            this.reason = reason;
            this.runTask();
          }
        });
      };

      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }

    // 执行promise队列任务
    runTask() {
      if (this.state === FULFILLED) {
        this.onResolveList.forEach((fn) => fn(this.value));
        this.onResolveList = [];
      }
      if (this.state === REJECTED) {
        this.onRejectList.forEach((fn) => fn(this.reason));
        this.onRejectList = [];
      }
    }

    then(onFulfilled, onRejected) {
      // new MyPromise then 的链式调用
      return new MyPromise((resolve, reject) => {
        this.onResolveList.push((res) => {
          // onFulfilled为空处理
          if (onFulfilled === null || onFulfilled === undefined) {
            resolve(res);
            return;
          }

          try {
            resolve(onFulfilled(res));
          } catch (error) {
            reject(onFulfilled(res));
          }
        });

        this.onRejectList.push((res) => {
          // onRejected为空处理
          if (onRejected === null || onRejected === undefined) {
            reject(res);
            return;
          }

          try {
            resolve(onRejected(res));
          } catch (error) {
            reject(onRejected(res));
          }
        });

        this.runTask();
      });
    }

    catch(onRejected) {
      return this.then(null, onRejected);
    }

    // finally本质上是then方法的特例。只是then是需要状态改变后的结果而finally不需要。
    finally(cb) {
      return this.then(
        (res) => {
          cb();
          return res;
        },
        (res) => {
          cb();
          throw res;
        }
      );
    }

    static resolve(value) {
      return new MyPromise((resolve, reject) => {
        // 如果是promise直接调用then方法把结果resolve，否者直接resolve结果
        if (value instanceof MyPromise) {
          value.then(
            (res) => {
              resolve(res);
            },
            (err) => {
              resolve(err);
            }
          );
          return;
        }
        resolve(value);
      });
    }

    static reject(err) {
      return new MyPromise((resolve, reject) => reject(err));
    }

    static all(promises) {
      return new MyPromise((resolve, reject) => {
        // 参数判断
        if (!Array.isArray(promises)) {
          throw new TypeError('promises must be an array');
        }

        const result = []; // 存放结果
        let count = 0; // 记录有几个FULFILLED

        promises.forEach((promise, index) => {
          let p =
            promise instanceof MyPromise ? promise : MyPromise.resolve(promise);

          p.then((res) => {
            result[index] = res;
            count++;
            // 判断是否已经完成
            if (count === promises.length) {
              resolve(result);
            }
          }).catch((err) => {
            reject(err);
          });
        });
      });
    }

    static allSettled(promises) {
      return new MyPromise((resolve, reject) => {
        let result = [];
        let count = 0;
        promises.forEach((promise, index) => {
          let p =
            promise instanceof MyPromise ? promise : MyPromise.resolve(promise);

          p.then(
            (res) => {
              result[index] = { status: 'fulfilled', value: res };
            },
            (err) => {
              result[index] = { status: 'rejected', reason: err };
            }
          ).finally(() => {
            count++;
            // 这里将结果吐出去
            if (count === promises.length) {
              resolve(result);
            }
          });
        });
      });
    }

    // 只要有一个实例状态改变，包装实力就会变成这个改变状态的结果状态
    static race(promises) {
      return new MyPromise((resolve, reject) => {
        promises.forEach((promise) => {
          promise.then(resolve).catch(resolve);
        });
      });
    }

    // 和all正好相反
    // 只要参数实例有一个变成fulfilled状态，包装实例就会变成fulfilled状态
    // 如果所有参数实例都变成rejected状态，包装实例就会变成rejected状态。
    static any(promises) {
      return new MyPromise((resolve, reject) => {
        const errors = [];
        let count = 0;
        promises.forEach((promise, index) => {
          let p =
            promise instanceof MyPromise ? promise : MyPromise.resolve(promise);

          p.then(resolve).catch((value) => {
            count++;
            errors[index] = value;
            if (count === promises.length) {
              reject(new AggregateError(errors, 'All promises were rejected'));
            }
          });
        });
      });
    }
  }

  // 异步测试
  new MyPromise((resolve, reject) => {
    console.log('start');
    resolve(1);
  }).then((res) => {
    console.log('then');
    console.log(res);
  });
  console.log('end');

  // 链式调用测试
  let p = new MyPromise((resolve, reject) => {
    console.log('done');
    reject('出错了');
  })
    .then((tip) => {
      console.log('then1', tip);
      return 222;
    })
    .then((tip) => {
      console.log('then2', tip);
    })
    .then((d) => {
      console.log('dd>>>', d);
    })
    .catch((e) => {
      console.log('err>>>', e);
    })
    .then((d) => {
      console.log('dd2>>>', d);
      return 'dd2';
    })
    .finally(() => {
      console.log('onDone');
      return 'onDone';
    })
    .then((d) => {
      console.log(d);
    });

  // resolve 静态方法测试
  let p_resolve = MyPromise.resolve(1).then((res) => {
    console.log(res);
  });

  // reject 静态方法测试
  let p_reject = MyPromise.reject(Error(1)).catch((err) => {
    console.log(err);
  });

  function test() {
    return new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve(3);
      }, 2000);
    });
  }

  // all测试
  MyPromise.all([1, 2, test(), 4, MyPromise.reject(5)])
    .then((res) => {
      console.log('res: ', res);
    })
    .catch((err) => {
      console.log('err: ', err);
    });

  // allSettled测试
  let ps = [
    MyPromise.resolve(1),
    MyPromise.resolve(2),
    MyPromise.reject(3),
    4,
    5,
  ];
  MyPromise.allSettled(ps).then((res) => {
    console.log(res);
  });

  // race测试
  let p_race = MyPromise.race([
    MyPromise.resolve(1),
    MyPromise.reject(Error(2)),
  ])
    .then((res) => {
      console.log('res: ', res);
    })
    .catch((err) => {
      console.log('err: ', err);
    });

  // any测试
  let p_any = MyPromise.any([
    1,
    MyPromise.reject(2),
    MyPromise.reject(Error(3)),
  ])
    .then((res) => {
      console.log('res: ', res);
    })
    .catch((err) => {
      console.log('err: ', err);
    });
}
